Pseudo-BNF description of translated OPL (.OPO/.OPA) file format
Deduced by Mike Rudin Aug. 93
Series 5 (OPL32, AIF, MBM) details added 28/3/98

Most numbers are shown in hex, so e.g. "0000" or "00 00" is two bytes.
Decimal numbers are shown with a (d) suffix.

-------------------------------------------------------------

Pseudo-BNF brackets:
<>  comment
[]  optional
[]+ one or more
{}  zero or more

-------------------------------------------------------------

<Note, in the following, "T+V" means the length of the Ascii text, plus
the length of the TextSize Varint field>

TextEdFile := <Series 5 OPL source code format>
TextEdHeader
Varint  <TextSize>
{Byte}  <Ascii text, TextSize bytes.  Note 06 is code for newline>
TextEdTrailer
.

TextEdHeader :=
Longint <TextEd uid1, always 10000037 (KDirectFileStoreLayoutUid)>
Longint <TextEd uid2, always 1000006d>
Longint <TextEd uid3, always 10000085>
Longint <TextEd uid4 checksum, always 550863F4>
Longint <T+V plus 202(d)>
Longint <always 1000005C>
Longint <always 10000063>
Longint <always 00000000>
Longint <always 10000064>
.

TextEdTrailer := <191(d) bytes; many of unknown function>
01 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00
01 00 00 00
D0 02 00 00 D0 02 00 00
A0 05 00 00 A0 05 00 00 A0 05 00 00 A0 05 00 00 
01 00 00 00 00 00 00 00 00 00 00 00 00
5C 00 00 10 63 00 00 10
Longint <T+V plus 36(d)>
65 00 00 10 00 00 00 00 66 00 00 10 00 00 00 00
64 00 00 10 02 06 01 00 00 00 00 00 00 00 00 00
00 00 00
5C 00 00 10 63 00 00 10
Longint <T+V plus 44(d)>
65 00 00 10 00 00 00 00 66 00 00 10 00 00 00 00
64 00 00 10 02 06 FD 00 00 10 82 2E 00 00 C6 41
00 00 00
85 00 00 10
"*TextEd.app" 06 <11 characters plus newline>
85 00 00 10 14 00 00 00 05 01 00 10
Longint <T+V plus 52(d)>
89 00 00 10
Longint <T+v plus 187(d)>
.

<OPL32 AIF file format follows:>
AIFfile :=
Longint <AIF uid1, always 10000037 (KDirectFileStoreLayoutUid)>
Longint <AIF uid2, always 1000006a (KUidAppInfoFile)>
Longint <AIF uid3, have seen 10000757, 10000429, and 10003ddc>
Longint <AIF uid4 checksum{?}>
Longint <AIFTrailer address>
{AIFCaptionString | AIFmbm}
AIFTrailer .

Varint :=  <variable-length integer>
[Byte]+ .
<1st Byte of Varint indicates total no. of bytes and
scaling factor by position of least sig. '0' bit.
No. of bytes  Scale  bit suffix   hex example
4             8      011          13 00 00 00  means '2'
2             4      01           09 00        means '2'
1             2      0            04           means '2'>

AIFTrailer :=
Varint <number of captions>
[AIFCaptionIndex] <6-byte caption entries>
Varint <number of mbms>
[AIFmbmIndex] <6-byte mbm icon entries>
01 00 00 00   <4 bytes>
Int <non-zero if FLAGS includes KFlagsAppFileBased%>
00 00   <2 bytes>
Int <non-zero if FLAGS includes KFlagsAppIsHidden%>
00 00 00 00 00 00 . <6 bytes>

AIFCaptionIndex :=
Longint <file address of AIFCaptionString>
Int .   <Language code>

AIFCaptionString :=
Byte       <string length * 4 + 2>
[Char]+ .  <string chars>

AIFmbmIndex :=
LongInt <file address of AIFmbm>
Int .   <pixel size of square mbm, e.g. 32(d) for a 32x32 icon>

AIFmbm :=
MBMBitmap <Mask bitmap for icon>
MBMBitmap <Icon graphics>
.

<.MBM (multi-bitmap) file format follows:>
MBMfile :=
Longint <00 MBM uid1, 10000037 (KDirectFileStoreLayoutUid)>
Longint <04 MBM uid2, 10000042>
Longint <08 MBM uid3, 00000000>
Longint <0C MBM uid checksum(?)>
Longint <10 MBMTrailer address>
{MBMSection}
.

MBMSection := MBMBitmap | MBMTrailer .

MBMBitmap :=
{Byte} <length bmLen&>
.

MBMTrailer :=
Longint <00000002 no of bitmaps, incl. mask>
Longint <00000020 addr of 1st MBMBitmap>
Longint <00000108 addr of 2nd MBMBitmap>
<etc>
.

<OPL32 .APP file format follows>
App32File :=
Longint <APP uid1, always 10000037 (KDirectFileStoreLayoutUid)>
Longint <APP uid2, 10000073 (KUidOPO) or 10000074 (KUIDOplApp)>
Longint <APP uid3>
Longint <APP uid4 checksum(?)>
Longint <App32Trailer address>
String <source code file pathname, may be zero-terminated>
[ ProcBody | ProcDeclTable32 | OXHtable ]+
App32Trailer .

App32Trailer :=
Longint <RTI UID>
Int <translator version>
Int <required runtime interpreter (RTI) version>
Longint <Address of Program Name>
Longint <Address of ProcDeclTable32>
Longint <Address of OXHtable, or 0 if no table present>
00 00 .

OXHtable :=
Int <Number of OXH files referenced>
[
String <Pathname of .OXH, without ".oxh" suffix>
Longint <UID of OPX>
Int <Version of OPX>
]+  .

<OPL16 .OPO or .OPA file format follows>
OPLObjectFile :=
"OPLObjectFile**" 00 <16 bytes total zero-terminated file signature string>
0100 <file version?>
Int <address of flength>
String <source code file pathname, may be zero-terminated>
[APPdetails] <present if flength address above does not match here>
LongInt <flength: no. of bytes in file>
(0f110f11 | <translator version, required runtime interpreter (RTI) version>
 1f111f11)  <s3a code>
LongInt <file addr of name string for main proc. in file>
[ProcBody]+
ProcDeclTable16 .

ProcDeclTable16 :=
[ProcDecl]+
00 00 .

ProcDeclTable32 :=
[ProcDecl]+
00 .

Int <2's complement> := Byte <LSB> Byte <MSB> .

LongInt <2's complement> := Byte <LSB> Byte Byte Byte <MSB> .

String := Byte <length n> {Char <n times>} .

APPdetails :=
Int <icon picture size, if present>
Icon
Int <space for rest of details>
APP_EXT_str
00 <always>
PATH_str
Int <APP type, eg $1003 for S3a TYPE $1003>.

Icon := {Byte} <optional icon picture, copied verbatim from file
specified in ICON statement> .

APP_EXT_str :=
{Char} <up to 8 chars spec. in APP statement>
'.' <as in appname.ext>
{Char} <up to 3 chars spec. in EXT statement>
[00]+ <pad APP_EXT_str to 13 bytes> .

PATH_str :=
{Char} <what's specified in PATH minus any trailing '\'>
'\'
[00]+ <pad PATH_str out to 20 bytes> .

TypeCode :=
Byte <00 = Int, 01 = LongInt, 02 = Float, 03 = String,
 80 = array of Ints etc.> .

ProcBody :=
[Int] <to do with procedure cacheing, for Series 3a and OPL32 only>
Int <Data_Size, no of dynamic bytes needed for decl/vars, 1200 or more>
Int <QCODE_Size, no of q-code bytes>
    <N.B. RTI allocates cell Data_Size+QCODE_Size in size>
Int <maximum stack space needed at runtime>
Params
GlobalDecls
ProcRefs
{ExtGlobalRef}
00  <end of external global refs>
{StringLocLen}
00 00 <end of StringLocLen>
{ArrayLocSiz}
00 00 <end of ArrayLocSiz>
[Byte]+ <Q-code bytes, usu. finishing with 76> .

Params :=
Byte <number of params to this proc>
{TypeCode} <in reverse order of declaration> .

GlobalDecls :=
Int <no. of bytes for rest of GlobalDecls>
{String <name of global var>
TypeCode
Int <location in dynamic space for global var> }.

ProcRefs :=
Int <no. of bytes for rest of ProcRefs>
{String <Procedure name>
Byte <no. of params in ref>} .

ExtGlobalRef := <each needs 2 dyn. bytes>
String <external Global var name, incl. trailing '%' etc.>
TypeCode .

StringLocLen :=
Int <location in dynamic space of string var>
Byte <string var max length> .

ArrayLocSiz :=
Int <addr in dynamic space of array var>
Int <no. of elements> .

ProcDecl :=
String <PROC name, without trailing ':'>
LongInt <file addr of ProcBody>
Int <line number of PROC in source file (starts at 0)> .

-------------------------------------------------------------

When a PROC is executed, its local data and global refs are put into
what I have called 'dynamic space', probably on the stack.  References
to local data are encoded in the Q-code as offsets into this dynamic
space.

Overall usage of dynamic space, from 0012 upwards (with Revtran
variables marking boundaries):
1. space for locally-declared global name text: 3 bytes plus name string
   (incl. length field) for each global declared.  Not referenced in Qcode.

pr-accessed:
2. space for references to procs: 1 byte plus name string (w/o ':' but
   with length field) for each unique proc. referenced.

EndPr&
eVar-accessed:
3. 2 bytes per proc. param
EndPrm& - in practice always under 7FFF
4. space for each external global: 2 bytes each.

lVar-accessed:
5. locally-declared global variables (lengths depend on type as above)
EndGlDc& - can be over 7FFF
6. local variables (lengths depend on type as above)
EndDyn& - can be over 7FFF

-------------------------------------------------------------

Globals and locals have dynamic space allocated in 
the same way:

int%, longint&, float:
2, 4 or 8 bytes.
Start address referred to in global decl. and in q-code.

string$(n):
1+n bytes for string itself, preceded by 1 extra byte.
Length field address referred to in global decl. and in q-code.
Extra byte address referred to in strings loc/length section.

int%(n), longint&(n), float(n):
2 extra bytes plus n * (2, 4 or 8).
1st element address referred to in global decl. and in q-code.
1st extra byte address referred to in array loc/size section.

string$(n,m):
2 extra bytes for array, plus 1 extra byte for 1st element,
plus n * (1+m).
1st element address referred to in global decl. and in q-code.
Extra byte of 1st element referred to in strings loc/length section.
1st extra byte address referred to in array loc/size section.

-------------------------------------------------------------

A note about local declarations:

At runtime, when a procedure is called, a contiguous area of
memory is allocated for storage of the procedure's local
variables.  The Q-code describes the size of that storage area,
but does not explicitly declare the locals.  The procedure body
merely makes references to numbered locations within the area.
Revtran must create local declarations which, on re-translation,
will result in the same locations being used, in the same sized
storage area, as in the original.  This is important in cases
such as the example for 'OS' in the Series 3 Programming Manual.
After Revtran has searched the Q-code for references to locations
in dynamic storage, it can detect any 'gaps' where storage exists
but is not referenced.  It creates dummy local declarations to
fill those gaps, using an integer for a 2-byte gap, and one or
more strings for a bigger gap (each string of n characters taking
n+2 bytes of storage).

[End of BNF.TXT]
